import Vue from "vue"
import {Decrypt, Encrypt, transformToTreeFormat} from "./tools"

import HttpRequest from "./request"
import config from '../config/index'

const axios = new HttpRequest(config.getBaseURL())

import qs from 'qs'

Vue.prototype.$transformToTreeFormat = transformToTreeFormat

/**
 * this.$id('key')
 * @type {function(*=)}
 */
Vue.prototype.$id = ((time) => (key = 'id') => key + (time++))(Date.now())

/**
 * Delay func
 * @type {(function(*=, *=): void)|*}
 */
Vue.prototype.$timeOut = ((to, t_) => (fn, time) => {
    to && (clearTimeout(to)), to = setTimeout(fn, time || t_)
})(null, 100)


Vue.prototype.$docHeight = (n = 0) => {
    let clientHeight = 0;
    if (document.body.clientHeight && document.documentElement.clientHeight) {
        clientHeight = (document.body.clientHeight < document.documentElement.clientHeight) ? document.body.clientHeight : document.documentElement.clientHeight;
    } else {
        clientHeight = (document.body.clientHeight > document.documentElement.clientHeight) ? document.body.clientHeight : document.documentElement.clientHeight;
    }
    return clientHeight + n;
}

Vue.prototype.$encrypt = Encrypt

Vue.prototype.$decrypt = Decrypt

/**
 * this.$eval(jsonStr)
 * @type {function(*): *}
 */
const $eval = Vue.prototype.$eval = (($Function) => (code) => new $Function(`return ${code}`)())(Function)

/**
 * http
 * @type {function(*, *=, *=, *=, *=, *): AxiosPromise<any>}
 */
const $ajax = Vue.prototype.$ajax = (
    url,
    data = {},
    params = {},
    method = 'GET',
    header = 'application/x-www-form-urlencoded',
    transformResponse
) => {
    return axios.request({
        url,
        data: qs.stringify(data),
        params,
        method,
        header,
        transformResponse
    })
}

/**
 *
 * @param url
 * @param data
 * @param header
 * @param transformResponse
 * @returns {AxiosPromise<*>}
 */
Vue.prototype.$post = (
    url,
    data = {},
    header = 'application/x-www-form-urlencoded',
    transformResponse
) => {
    return $ajax(
        url,
        data,
        {},
        "POST",
        header,
        transformResponse)
}

/**
 * parse jsonString
 * @type {function(*=): any}
 */
const $parse = ((onstart, onend, importName) => {
    Vue.prototype.$stringify = (object, includeFunction) => {
        if (includeFunction) {
            // 处理json里面的函数体
            return JSON.stringify(object, (key, val) => {
                if (typeof val === 'function') {
                    if (val.name === importName) {
                        return `${onstart} ${importName}('${val.getId()}')${onend}`
                    }
                    let funcStr = val.toString()
                    // 处理  data() {}  但要排除 () => {} 这种结构的函数
                    if (val.toString().indexOf(val.name) === 0) {
                        funcStr = `function ${funcStr}`
                    }
                    return `${onstart} ${funcStr}${onend}`
                }
                return val
            })
        } else {
            return JSON.stringify(object)
        }
    }

    return Vue.prototype.$parse = (string) => {
        return JSON.parse(string, (k, v) => {
            if (v && v.indexOf && v.indexOf(onstart) === 0) {
                return $eval(v.substring(onstart.length, v.length - onend.length))
            }
            return v
        })
    }
})('(function(){ return', '})()', '$import')

const getOptions = (eleVue) => {
    if (typeof eleVue === 'string') {
        eleVue = $parse(eleVue)
    }
    let {options = {}} = eleVue
    let {components = {}} = options
    for (let key in components) {
        if (typeof components[key] !== 'string') {
            continue
        }
        components[key] = $eval(components[key])
    }
    return options
}

/**
 * 解密
 * @param data
 * @returns {*}
 */
const transformResponse = data => {
    if (typeof data === 'string') {
        try {
            data = Decrypt(data)
        } catch (e) { /* Ignore */
        }
        data = $parse(data)
    }
    return data
}

export const createStyle = (opts, id) => {
    if (!opts || !opts.style) {
        return
    }
    id = 'style-' + id
    let style = document.getElementById(id)
    style && style.remove()
    style = document.createElement('style')
    style.id = id
    // style.type = 'text/css'
    style.append(opts.style)
    document.head.appendChild(style)
}

/**
 *
 * @type {function(*=, *=): {}}
 */
const $buildElement = Vue.prototype.$buildElement = (ele, id) => {
    let o = getOptions(ele, id)
    if (o.styles) {
        o.styles.forEach(id => $eval(`$import("${id}")(() => {}, () => {})`))
    }
    createStyle(o, id)
    return o
}

/**
 *组件异步导入方法
 *
 * @type {function(*=): Import}
 */
window.$import = Vue.prototype.$import = ((cache, resolves, cf) => (id, content) => {
    const Import = (resolve, reject) => {
        if (cf(cache, id, content) || cache[id]) {
            resolve(content ? $buildElement(cache[id], id) : getOptions(cache[id], id))
        } else {
            if (resolves[id]) {
                resolves[id].push(resolve)
            } else {
                resolves[id] = [resolve]
                let url = `/sys/ele/ins/${id}.json`.replace(/\/+/g, '/')
                $ajax(url, {}, null, 'GET', null,
                    [transformResponse]).then(({data}) => {
                    cf(cache, id, data)
                }).then((r, o) => {
                    while ((r = resolves[id].shift())) {
                        (o = $buildElement(cache[id], id)) && r(o)
                    }
                }).catch(reject)
            }
        }
    }
    Import.getId = () => id
    Import.toString = () => {
        return 'ele' + id
    }
    return Import
})({}, {}, (cache, id, data) => {
    !!data && (cache[id] = data)
    return !!data
})

export default Vue
